'use strict';

exports.__esModule = true;

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };

var _graphql = require('graphql');

var _base = require('./base');

var _base2 = _interopRequireDefault(_base);

var _utils = require('./utils');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* flow */


// As per the GraphQL Spec, Integers are only treated as valid when a valid
// 32-bit signed integer, providing the broadest support across platforms.
//
// n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because
// they are internally represented as IEEE 754 doubles.
var MAX_INT = 2147483647;
var MIN_INT = -2147483648;

function checksize(limit, comparator, isValidNum) {
  return function (value) {
    if (isValidNum(value)) {
      return value;
    }
    throw new TypeError('Number must be ' + comparator + ' ' + limit + ', got ' + value);
  };
}

/**
 * Number scalar type that represents numerical data.
 * By itself, it is essentially the `GraphQLFloat` type.
 */

var NumberScalar = function (_Base) {
  _inherits(NumberScalar, _Base);

  function NumberScalar() {
    _classCallCheck(this, NumberScalar);

    return _possibleConstructorReturn(this, (NumberScalar.__proto__ || Object.getPrototypeOf(NumberScalar)).apply(this, arguments));
  }

  _createClass(NumberScalar, [{
    key: 'min',


    /**
     * Specifies the minimum number allowed (inclusive).
     */
    value: function min(limit) {
      if (limit > this._max || limit >= this._lesser) {
        throw new RangeError('limit cannot be larger than the largest allowed value');
      }
      if (limit <= this._greater) {
        throw new RangeError('limit cannot be smaller or equal to the smallest allowed value');
      }
      var obj = this.clone();
      obj._min = limit;
      obj._func.push(checksize(limit, 'larger or equal to', function (size) {
        return size >= limit;
      }));
      return obj;
    }

    /**
     * Specifies the maximum number allowed (inclusive).
     */

  }, {
    key: 'max',
    value: function max(limit) {
      if (limit < this._min || limit <= this._greater) {
        throw new RangeError('limit cannot be smaller than the smallest allowed value');
      }
      if (limit >= this._lesser) {
        throw new RangeError('limit cannot be larger or equal to the largest allowed value');
      }
      var obj = this.clone();
      obj._max = limit;
      obj._func.push(checksize(limit, 'smaller or equal to', function (size) {
        return size <= limit;
      }));
      return obj;
    }

    /**
     * Specifies that the value must be greater than the limit.
     */

  }, {
    key: 'greater',
    value: function greater(limit) {
      if (limit >= this._lesser || limit >= this._max) {
        throw new RangeError('limit cannot be larger or equal to the largest allowed value');
      }
      if (limit < this._min) {
        throw new RangeError('limit cannot be smaller than the smallest allowed value');
      }
      var obj = this.clone();
      obj._greater = limit;
      obj._func.push(checksize(limit, 'larger than', function (size) {
        return size > limit;
      }));
      return obj;
    }

    /**
     * Specifies that the value must be lesser than the limit.
     */

  }, {
    key: 'lesser',
    value: function lesser(limit) {
      if (limit <= this._min || limit <= this._greater) {
        throw new RangeError('limit cannot be smaller or equal to the smallest allowed value');
      }
      if (limit > this._max) {
        throw new RangeError('limit cannot be larger than the largest allowed value');
      }
      var obj = this.clone();
      obj._lesser = limit;
      obj._func.push(checksize(limit, 'smaller than', function (size) {
        return size < limit;
      }));
      return obj;
    }

    /**
     * Requires the number to be an integer from -(2^31) and 2^31 - 1.
     * As per the GraphQL Spec, Integers are only treated as valid when a valid
     * 32-bit signed integer, providing the broadest support across platforms.
     */

  }, {
    key: 'integer',
    value: function integer() {
      var obj = this.clone();
      obj._isInt = true;
      return obj;
    }

    /**
     * Specifies the maximum number of decimal places allowed.
     */

  }, {
    key: 'precision',
    value: function precision(limit) {
      if (!Number.isInteger(limit) || limit <= 0) {
        throw new Error('limit must be a positive integer');
      }
      var obj = this.clone();
      obj._func.push(function (value) {
        var places = value.toString().match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
        var decimals = Math.max((places[1] ? places[1].length : 0) - (places[2] ? parseInt(places[2], 10) : 0), 0);
        if (decimals <= limit) {
          return value;
        }
        throw new TypeError('Number is limited to ' + limit + ' decimal places');
      });
      return obj;
    }

    /**
     * Specifies that the number must be a multiple of base.
     */

  }, {
    key: 'multiple',
    value: function multiple(base) {
      var obj = this.clone();
      obj._func.push(function (value) {
        if (value % base === 0) {
          return value;
        }
        throw new TypeError('Number must be a multiple of ' + base);
      });
      return obj;
    }

    /**
     * Specifies that the number must be positive (>0).
     */

  }, {
    key: 'positive',
    value: function positive() {
      return this.greater(0);
    }

    /**
     * Specifies that the number must be negative (<0).
     */

  }, {
    key: 'negative',
    value: function negative() {
      return this.lesser(0);
    }
  }, {
    key: 'create',
    value: function create() {
      if (this._isInt) {
        this._func.push(function (value) {
          if (value <= MAX_INT && value >= MIN_INT) {
            return (value < 0 ? Math.ceil : Math.floor)(value);
          }
          throw new TypeError('Int cannot represent non 32-bit signed integer value: ' + value);
        });
      }
      function rejectEmptyStr(value) {
        if (value === '') {
          throw new TypeError('Int cannot represent non 32-bit signed integer value: (empty string)');
        }
        return value;
      }

      var coerce = (0, _utils.compose)([Number, rejectEmptyStr].concat(_toConsumableArray(this._func)));
      this.serialize = coerce;
      this.parseValue = coerce;
      var kind = this._isInt ? _graphql.Kind.INT : _graphql.Kind.FLOAT;
      this.parseLiteral = function (ast) {
        return ast.kind === kind ? coerce(ast.value) : null;
      };
      return _get(NumberScalar.prototype.__proto__ || Object.getPrototypeOf(NumberScalar.prototype), 'create', this).call(this);
    }
  }]);

  return NumberScalar;
}(_base2.default);

exports.default = NumberScalar;